<Html>
    <head>
        <title>数据同步问题  (远程库与本地库的数据同步问题)</title>
    </head>
    <body>
        <script>
                // 数据同步问题

                    /*
                        在 ABP框架中远程库与本地库的数据同步问题，"性能问题" 和 "锁冲突"  是常见挑战。

                        基于基于你的需求，我提供一套完整的优化方案，包括 "批量处理"、"事务优化" 和 ABP 框架特定的最佳实践。
                    */

                    /*
                        一、性能瓶颈分析

                                你的场景可能存在以下性能问题：

                                    1、 N+1 查询问题：  远程查询后，每条记录都要出发一次本地查询，这九域EF Core中延迟加载造成的N+1问题类似。

                                    2、长事务持有锁：   整个同步过程可能都在一个事务中，导致新建、修改 和 查询存在"锁冲突".

                                    3、缺少批量操作：   EF Core 默认逐条处理，数据库往返频繁

                                    4、重复查询：       已在内存中的实体可能被再次从数据库查询。

                    */

                    /*
                        二、优化方案：

                                1、批量查询与对比  (关键优化)

                                        public async Task SyncDataAsync()
                                        {
                                            // 1. 从远程库获取所有记录（使用无跟踪查询减少内存占用）
                                                var remoteEntities = await _remoteRepository.GetAll().AsNoTracking().ToListAsync();
                                                
                                                if (!remoteEntities.Any())
                                                {
                                                    return;
                                                }
                                            
                                            // 2. 提取所有Code（使用HashSet优化查找性能）
                                                var remoteCodes = new HashSet<string>(remoteEntities.Select(e => e.Code));
                                            
                                            // 3. 批量查询本地库中已存在的Code（一次数据库往返）
                                                var existingCodes = await _localRepository.GetAll()
                                                                    .Where(e => remoteCodes.Contains(e.Code))
                                                                    .Select(e => e.Code)
                                                                    .ToHashSetAsync();
                                            
                                            // 4. 分类处理：需要添加的和需要更新的
                                                var entitiesToAdd = remoteEntities
                                                    .Where(e => !existingCodes.Contains(e.Code))
                                                    .ToList();
                                                
                                                var entitiesToUpdate = remoteEntities
                                                    .Where(e => existingCodes.Contains(e.Code))
                                                    .ToList();
                                            
                                            // 5. 分批处理（避免一次性处理大量数据导致内存溢出）
                                                await ProcessEntitiesInBatches(entitiesToAdd, entitiesToUpdate);
                                        }  

                                2. 分批处理与小事务（减少锁持有时间）

                                        private async Task ProcessEntitiesInBatches(List<RemoteEntity> toAdd, List<RemoteEntity> toUpdate)
                                        {
                                            var batchSize = 500; // 根据数据量和数据库性能调整
                                            
                                            // 处理新增记录（分批提交）
                                            for (var i = 0; i < toAdd.Count; i += batchSize)
                                            {
                                                var batch = toAdd.Skip(i).Take(batchSize).ToList();
                                                
                                                await using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
                                                {
                                                    foreach (var entity in batch)
                                                    {
                                                        var newEntity = ObjectMapper.Map<RemoteEntity, LocalEntity>(entity);
                                                        await _localRepository.InsertAsync(newEntity, autoSave: false);
                                                    }
                                                    
                                                    await uow.CompleteAsync();
                                                }
                                            }
                                            
                                            // 处理更新记录（分批提交）
                                            for (var i = 0; i < toUpdate.Count; i += batchSize)
                                            {
                                                var batch = toUpdate.Skip(i).Take(batchSize).ToList();
                                                
                                                await using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
                                                {
                                                    foreach (var remoteEntity in batch)
                                                    {
                                                        // 使用NoTracking查询获取实体（避免EF Core跟踪重复实体）
                                                        var localEntity = await _localRepository.GetAll()
                                                            .AsNoTracking()
                                                            .FirstOrDefaultAsync(e => e.Code == remoteEntity.Code);
                                                        
                                                        if (localEntity != null)
                                                        {
                                                            // 手动映射更新字段（避免整个实体被标记为Modified）
                                                            localEntity.Name = remoteEntity.Name;
                                                            localEntity.Status = remoteEntity.Status;
                                                            
                                                            // 附加实体并标记为已修改
                                                            await _localRepository.UpdateAsync(localEntity, autoSave: false);
                                                        }
                                                    }
                                                    
                                                    await uow.CompleteAsync();
                                                }
                                            }
                                        }


                                3、优化查询与事务配置

                                        // 在模块中配置数据库连接和事务行为
                                        [DependsOn(typeof(AbpEntityFrameworkCoreSqlServerModule))]
                                        public class YourModule : AbpModule
                                        {
                                            public override void ConfigureServices(ServiceConfigurationContext context)
                                            {
                                                context.Services.AddAbpDbContext<YourDbContext>(options =>
                                                {
                                                    options.AddDefaultRepositories();
                                                });
                                                
                                                // 配置数据库连接
                                                Configure<AbpDbContextOptions>(options =>
                                                {
                                                    options.UseSqlServer(sqlServerOptions =>
                                                    {
                                                        // 配置查询超时（根据数据量调整）
                                                        sqlServerOptions.CommandTimeout(300);
                                                        
                                                        // 配置连接池
                                                        sqlServerOptions.EnableRetryOnFailure(
                                                            maxRetryCount: 5,
                                                            maxRetryDelay: TimeSpan.FromSeconds(30),
                                                            errorNumbersToAdd: null
                                                        );
                                                    });
                                                });
                                                
                                                // 配置工作单元默认行为
                                                Configure<AbpUnitOfWorkDefaultOptions>(options =>
                                                {
                                                    // 使用读提交隔离级别（减少锁冲突）
                                                    options.IsolationLevel = IsolationLevel.ReadCommitted;
                                                });
                                            }
                                        }

                                4、索引优化
                                                    
                                         确保作为条件的字段添加了索引
                    */
        </script>
    </body>
</Html>